package cate.game.server.configuration.dao;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.model.Filters;
import com.mongodb.client.model.ReplaceOptions;
import com.mongodb.client.result.InsertManyResult;
import com.mongodb.client.result.InsertOneResult;
import com.mongodb.client.result.UpdateResult;
import easy.java.practice.errorcode.EcException;
import easy.mongo.anno.MgMapField;
import easy.mongo.map.MgMapper;
import lombok.extern.slf4j.Slf4j;
import org.bson.BsonValue;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.repository.query.FluentQuery;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;

@Slf4j
public abstract class MgRepo<T, ID> implements MongoRepository<T, ID> {

    protected MongoCollection<Document> collection;

    public MgRepo init(MongoCollection<Document> collection) {
        this.collection = collection;
        return this;
    }

    protected abstract Class<T> getEntityClass();

    private static MgMapper mapper = MgMapper.createSpringStyle();

    protected MgMapper getMapper() {
        return mapper;
    }

    @Override
    public <S extends T> S save(S entity) {
        //log.info("要保存了 {}", entity);
        try {
            Document doc = getMapper().encode(entity);
            doc.remove(MgMapField.ID);
            Bson filter = Filters.eq(MgMapField.ID, transId(entity));
            ReplaceOptions options = new ReplaceOptions().upsert(true);
            UpdateResult ur = collection.replaceOne(filter, doc, options);
            if(ur.wasAcknowledged()) {
                handleAutoId(entity, ur.getUpsertedId());
            } else {
                log.warn("这家伙是 居然没有更新上？？？ {}", ur);
            }
            return entity;
        } catch (Exception e) {
            log.error("重点查看", e);
            throw new EcException(e);
        }
    }

    protected abstract <S extends T> Object transId(S entity);

    protected abstract <S extends T> void handleAutoId(S entity, BsonValue bv);

    @Override
    public <S extends T> List<S> saveAll(Iterable<S> entities) {
        if(entities == null) {
            return null;
        }
        try {
            List<S> result = new ArrayList<>();
            for(S item : entities) {
                result.add( save(item) );
            }
            return result;
        } catch (Exception e) {
            throw new EcException(e);
        }
    }

    @Override
    public Optional<T> findById(ID id) {
        //return Optional.empty();
        throw new EcException("尚未支持，严禁调用 public Optional<T> findById(ID id) ");
    }

    @Override
    public boolean existsById(ID id) {
        throw new EcException("尚未支持，严禁调用 public boolean existsById(ID id) ");
    }

    @Override
    public List<T> findAll() {
        MongoCursor<Document> iterator = collection.find().iterator();
        if(iterator == null) {
            return null;
        }
        List<T> list = new ArrayList<>();
        while (iterator.hasNext()) {
            Document doc = iterator.next();
            try {
                if(null != getEntityClass()) {
                    list.add( getMapper().decode(doc, getEntityClass()) );
                } else {
                    list.add( (T)getMapper().decode(doc) );
                }
            } catch (Exception e) {
                log.error("", e);
            }
        }
        return list;
    }

    @Override
    public Iterable<T> findAllById(Iterable<ID> ids) {
        throw new EcException("尚未支持，严禁调用 public Iterable<T> findAllById(Iterable<ID> ids) ");
    }

    @Override
    public long count() {
        return collection.countDocuments();
    }

    @Override
    public void deleteById(ID id) {
        throw new EcException("尚未支持，严禁调用 public void deleteById(ID id) ");
    }

    @Override
    public void delete(T entity) {
        throw new EcException("尚未支持，严禁调用 public void delete(T entity) ");
    }

    @Override
    public void deleteAllById(Iterable<? extends ID> ids) {
        throw new EcException("尚未支持，严禁调用 public void deleteAllById(Iterable<? extends ID> ids) ");
    }

    @Override
    public void deleteAll(Iterable<? extends T> entities) {
        throw new EcException("尚未支持，严禁调用 public void deleteAll(Iterable<? extends T> entities) ");
    }

    @Override
    public void deleteAll() {
        throw new EcException("尚未支持，严禁调用 public void deleteAll() ");
    }

    @Override
    public List<T> findAll(Sort sort) {
        Bson sorter = new Query().with(sort).getSortObject();
        MongoCursor<Document> iterator = collection.find().sort(sorter).iterator();
        if(iterator == null) {
            return null;
        }
        List<T> list = new ArrayList<>();
        while (iterator.hasNext()) {
            Document doc = iterator.next();
            try {
                if(null != getEntityClass()) {
                    list.add( getMapper().decode(doc, getEntityClass()) );
                } else {
                    list.add( (T)getMapper().decode(doc) );
                }
            } catch (Exception e) {
                log.error("", e);
            }
        }
        return list;
    }

    @Override
    public Page<T> findAll(Pageable pageable) {
        throw new EcException("尚未支持，严禁调用 public Page<T> findAll(Pageable pageable) ");
    }

    @Override
    public <S extends T> S insert(S entity) {
        try {
            Document doc = getMapper().encode(entity);
            if(doc.containsKey(MgMapField.ID)) {
                Object theId = transId(entity);
                if(theId != null) {
                    doc.put(MgMapField.ID, theId);
                }
            }
            InsertOneResult ior = collection.insertOne(doc);
            if(ior.wasAcknowledged()) {
                handleAutoId(entity, ior.getInsertedId());
            }
            return entity;
        } catch (Exception e) {
            throw new EcException(e);
        }
    }

    @Override
    public <S extends T> List<S> insert(Iterable<S> entities) {
        if(entities == null) {
            return null;
        }
        try {
            List<S> result = new ArrayList<>();
            List<Document> docList = new ArrayList<>();
            for(S item : entities) {
                if(item != null) {
                    Document doc = getMapper().encode(item);
                    docList.add(doc);
                    result.add(item);
                }
            }
            InsertManyResult imr = collection.insertMany(docList);
            Map<Integer, BsonValue> insertedIds = imr.getInsertedIds();
            //log.info("insertedIds={}", insertedIds);
            if(insertedIds != null) {
                int i = 0;
                for(S item : entities) {
                    BsonValue insertedId = insertedIds.get(i);
                    if(item != null && insertedId != null) {
                        handleAutoId(item, insertedId);
                    }
                    i++;
                }
            }
            return result;
        } catch (Exception e) {
            throw new EcException(e);
        }
    }

    @Override
    public <S extends T> Optional<S> findOne(Example<S> example) {
        //return Optional.empty();
        throw new EcException("尚未支持，严禁调用 public <S extends T> Optional<S> findOne(Example<S> example) ");
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example) {
        throw new EcException("尚未支持，严禁调用 public <S extends T> List<S> findAll(Example<S> example) ");
    }

    @Override
    public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
        throw new EcException("尚未支持，严禁调用 public <S extends T> List<S> findAll(Example<S> example, Sort sort) ");
    }

    @Override
    public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
        throw new EcException("尚未支持，严禁调用 public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) ");
    }

    @Override
    public <S extends T> long count(Example<S> example) {
        throw new EcException("尚未支持，严禁调用 public <S extends T> long count(Example<S> example) ");
    }

    @Override
    public <S extends T> boolean exists(Example<S> example) {
        throw new EcException("尚未支持，严禁调用 public <S extends T> boolean exists(Example<S> example) ");
    }

    @Override
    public <S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
        throw new EcException("尚未支持，严禁调用 public <S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) ");
    }

}
